package com.platform.system.system.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.platform.common.base.QueryHelpMybatisPlus;
import com.platform.common.exception.BadRequestException;
import com.platform.common.utils.*;
import com.platform.common.utils.enums.DataScopeEnum;
import com.platform.system.system.mapper.CompanyMapper;
import com.platform.system.system.mapper.DeptMapper;
import com.platform.system.system.mapper.UserMapper;
import com.platform.system.system.model.Company;
import com.platform.system.system.model.User;
import com.platform.system.system.model.dto.CompanyDto;
import com.platform.system.system.model.dto.CompanyQueryParam;
import com.platform.system.system.service.CompanyService;
import lombok.AllArgsConstructor;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;


@Service
@AllArgsConstructor
@CacheConfig(cacheNames = "company")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> implements CompanyService {

    private final UserMapper userMapper;
    private final RedisUtils redisUtils;
    private final DeptMapper deptMapper;

    @Override
    //@Cacheable
    public List<CompanyDto> queryAll(CompanyQueryParam query, Boolean isQuery) {
        if (isQuery) {
            query.setPidIsNull(true);
        }
        boolean notEmpty = StrUtil.isNotEmpty(query.getName()) || null != query.getPid()
                || CollectionUtils.isNotEmpty(query.getCreateTime());
        if (isQuery && notEmpty) {
            query.setPidIsNull(null);
        }
        return ConvertUtil.convertList(this.list(QueryHelpMybatisPlus.getPredicate(query)), CompanyDto.class);
    }

    public List<CompanyDto> queryAll2(CompanyQueryParam query, Boolean isQuery) throws Exception {
        String dataScopeType = SecurityUtils.getDataScopeType();
        if (isQuery) {
            if (dataScopeType.equals(DataScopeEnum.ALL.getValue())) {
                query.setPidIsNull(true);
            }
            List<Field> fields = QueryHelpMybatisPlus.getAllFields(query.getClass(), new ArrayList<>());
            List<String> fieldNames = new ArrayList<String>() {{
                add("pidIsNull");
                add("enabled");
            }};
            for (Field field : fields) {
                //设置对象的访问权限，保证对private的属性的访问
                field.setAccessible(true);
                Object val = field.get(query);
                if (fieldNames.contains(field.getName())) {
                    continue;
                }
                if (ObjectUtil.isNotNull(val)) {
                    query.setPidIsNull(null);
                    break;
                }
            }
        }
        QueryWrapper<Company> wrapper = QueryHelpMybatisPlus.getPredicate(query);
        wrapper.lambda().orderByAsc(Company::getCompanySort);
        List<CompanyDto> list = ConvertUtil.convertList(this.list(wrapper), CompanyDto.class);//deptMapper.toDto(deptRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), sort));
        return list;
    }

    @Override
    // @Cacheable
    public List<CompanyDto> queryAll() {
        return ConvertUtil.convertList(this.list(Wrappers.emptyWrapper()), CompanyDto.class);
    }

    @Override
    public Company getById(Long id) {
        return this.baseMapper.selectById(id);
    }

    @Override
    public CompanyDto findById(Long id) {
        return ConvertUtil.convert(getById(id), CompanyDto.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insert(Company resources) {
        boolean ret = this.save(resources);
        resources.setSubCount(0L);
        if (resources.getPid() != null) {
            redisUtils.del(CacheKey.Company_PID + resources.getPid());
            updateSubCnt(resources.getPid());
            // 清理缓存
            delCaches(resources.getId(), null, resources.getPid());
        }
        return ret;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean modifyById(Company resources) {
        // 旧的部门
        Long pidOld = getById(resources.getId()).getPid();
        if (resources.getPid() != null && resources.getId().equals(resources.getPid())) {
            throw new BadRequestException("上级不能为自己");
        }
        boolean ret = this.updateById(resources);
        updateSubCnt(pidOld);
        updateSubCnt(resources.getPid());
        // 清理缓存
        delCaches(resources.getId(), pidOld, resources.getPid());

        return ret;
    }

    private void updateSubCnt(Long companyId) {
        if (companyId == null) {
            return;
        }
        QueryWrapper<Company> query = new QueryWrapper<Company>();
        query.lambda().eq(Company::getPid, companyId);
        long count = this.count(query);

        UpdateWrapper<Company> update = new UpdateWrapper<Company>();
        update.lambda().eq(Company::getId, companyId);
        Company company = new Company();
        company.setSubCount(count);
        this.update(company, update);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(Long id) {
        Set<Long> ids = new HashSet<>(1);
        ids.add(id);
        return removeByIds(ids);
    }

    @Override
    public boolean deleteByIds(Set<Long> ids) {
        for (Long id : ids) {
            Company company = getById(id);
            // 清理缓存
            delCaches(id, company.getPid(), null);
            updateSubCnt(company.getPid());
            deptMapper.removeByCompanyId(id);
        }
        return this.removeByIds(ids);
    }

    // @Cacheable(key = "'pid:' + #p0")
    @Override
    public List<Company> findByPid(long pid) {
        QueryWrapper<Company> query = new QueryWrapper<>();
        query.lambda().eq(Company::getPid, pid);
        return this.list(query);
    }

    @Override
    public List<CompanyDto> getSuperior(CompanyDto deptDto, List<Company> depts) {
        if (deptDto.getPid() == null) {
            QueryWrapper<Company> query = new QueryWrapper<Company>();
            query.lambda().isNull(Company::getPid);
            depts.addAll(this.list(query));
            return ConvertUtil.convertList(depts, CompanyDto.class);
        }
        QueryWrapper<Company> query = new QueryWrapper<Company>();
        query.lambda().eq(Company::getPid, deptDto.getPid());
        depts.addAll(this.list(query));
        return getSuperior(ConvertUtil.convert(getById(deptDto.getPid()), CompanyDto.class), depts);
    }

    @Override
    public Map<String, Object> buildTree(List<CompanyDto> deptDtos) {
        Set<CompanyDto> trees = new LinkedHashSet<>();
        Set<CompanyDto> depts = new LinkedHashSet<>();
        List<String> deptNames = deptDtos.stream().map(CompanyDto::getName).collect(Collectors.toList());
        boolean isChild;
        for (CompanyDto deptDTO : deptDtos) {
            isChild = false;
            if (deptDTO.getPid() == null) {
                trees.add(deptDTO);
            }
            for (CompanyDto it : deptDtos) {
                if (it.getPid() != null && it.getPid().equals(deptDTO.getId())) {
                    isChild = true;
                    if (deptDTO.getChildren() == null) {
                        deptDTO.setChildren(new ArrayList<>());
                    }
                    deptDTO.getChildren().add(it);
                }
            }
            if (isChild) {
                depts.add(deptDTO);
            } else {
                Company dept = null;
                if (null != deptDTO.getPid()) {
                    dept = this.getById(deptDTO.getPid());
                }
                if (null != dept && !deptNames.contains(dept.getName())) {
                    depts.add(deptDTO);
                }
            }
        }

        if (CollectionUtils.isEmpty(trees)) {
            trees = depts;
        }

        Map<String, Object> map = new HashMap<>(2);
        map.put("totalElements", deptDtos.size());
        map.put("content", CollectionUtils.isEmpty(trees) ? deptDtos : trees);
        return map;
    }

    @Override
    public Set<Long> getDeleteCompanys(List<Company> deptList, Set<Long> deptIds) {
        for (Company dept : deptList) {
            deptIds.add(dept.getId());
            List<Company> depts = findByPid(dept.getId());
            if (depts != null && depts.size() != 0) {
                getDeleteCompanys(depts, deptIds);
            }
        }
        return deptIds;
    }

    @Override
    public List<Long> getCompanyChildren(Long deptId, List<Company> deptList) {
        List<Long> list = new ArrayList<>();
        deptList.forEach(dept -> {
            if (dept != null && dept.getEnabled()) {
                QueryWrapper<Company> query = new QueryWrapper<>();
                query.lambda().eq(Company::getPid, dept.getId());
                List<Company> depts = this.list(query);
                if (depts.size() != 0) {
                    list.addAll(getCompanyChildren(dept.getId(), depts));
                }
                list.add(dept.getId());
            }
        });
        return list;
    }

    @Override
    public void verification(Set<Long> companyIds) {
        if (userMapper.countByCompany(companyIds) > 0) {
            throw new BadRequestException("所选公司存在用户关联，请解除后再试！");
        }
        if (deptMapper.countByCompany(companyIds) > 0) {
            throw new BadRequestException("所选公司存在部门关联，请解除后再试！");
        }
    }

    @Override
    public void download(List<CompanyDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (CompanyDto dept : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("上级部门", dept.getPid());
            map.put("子部门数目", dept.getSubCount());
            map.put("名称", dept.getName());
            map.put("排序", dept.getCompanySort());
            map.put("状态", dept.getEnabled());
            map.put("创建者", dept.getCreateBy());
            map.put("更新者", dept.getUpdateBy());
            map.put("创建日期", dept.getCreateTime());
            map.put("更新时间", dept.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    /**
     * 清理缓存
     *
     * @param id /
     */
    public void delCaches(Long id, Long pidOld, Long pidNew) {
        List<User> users = userMapper.findByRoleDeptId(id);
        // 删除数据权限
        redisUtils.delByKeys(CacheKey.DATA_USER, users.stream().map(User::getId).collect(Collectors.toSet()));
        redisUtils.del(CacheKey.DEPT_ID + id);
        redisUtils.del(CacheKey.DEPT_PID + (pidOld == null ? 0 : pidOld));
        redisUtils.del(CacheKey.DEPT_PID + (pidNew == null ? 0 : pidNew));
    }

}
